Skip to content

CPP MOLE 2.0 grid implementation + error handling - #442

Closed
Tony-Drummond wants to merge 42 commits into
dev/MOLE_2.0from
cpp_dev/mole2_0_grid_implementation
Closed

Tony-Drummond wants to merge 42 commits into
dev/MOLE_2.0from
cpp_dev/mole2_0_grid_implementation

Conversation

@Tony-Drummond

Copy link
Copy Markdown
Collaborator

What type of PR is this? (check all applicable)

  • Refactor
  • Feature
  • Bug Fix
  • Optimization
  • Example
  • Documentation

Description

Objective
Bring the C++ implementation into compliance with the MOLE 2.0
API, numerical behavior, testing, documentation, and packaging standards.

mole/
├── cpp/
| │── cmake/
| │── doc/
| │── examples
| |── src/
| │ ├── boundaries
| │ └── grids
| │ └── include
| │ └── operators
| │ └── sys
| │ └── utils
| |── tests
Figure 1. C++ MOLE 2.0 implementation. Bolded subdirectories names are the ones impacted by this PR and related Git issues.
This PR implements first steps of CPP MOLE 2.0 implementation:

  1. Adopt new CPP software organization into the directory structure in Figure 1 (below)
  2. Implement MOLE Errors handling and reporting system.
  3. Implement C++ regression tests for the MOLE Errors module and use examples for documentation
  4. Implement MOLE Grid classes and related data structures
  5. Implement C++ regression tests for the MOLE Grid classes + Error handling/tracking, and basic examples for documentation

Related Issues & Documents

Issues, #437 (cpp directory reorganization) , #438 (error handling), #440 (cpp grid implementation)

QA Instructions, Screenshots, Recordings

Tests and examples for creating grid are included. There are several tests of the full CPP functionality included here.

To build:

  1. cd mole/cpp (notice that while we are moving from MOLE 1.2 to MOLE 2.0, the new CPP development is organized using the directory structure shown above). Thus, to build the C++, start from the subdirectory mole/cpp
  2. mkdir build; cd build create a new subdirectory build and enter that new subdirectory
  3. cmake .. will prepare to build the MOLE cpp library and include all software dependencies
  4. make will manually build the MOLE library, examples and tests
  5. cd examples/grids to run different examples of grid creation, or
  6. cd tests to run any of the different tests of the library (>120 tests of the current C++ MOLE API)

Keep-open request

  • I am requesting maintainer review for keep-open.

Reason:

Added/updated tests?

_We encourage you to test all code included with MOLE, including examples.

  • Yes
  • No, and this is why: please replace this line with details on why tests
    have not been included
  • I need help with writing tests

Read Contributing Guide and Code of Conduct

[optional] Are there any post deployment tasks we need to perform?

[optional] What gif best describes this PR or how it makes you feel?

valeriabarra and others added 24 commits August 11, 2026 21:42
Bumps [pillow](https://github.com/python-pillow/Pillow) from 12.1.1 to 12.3.0.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](python-pillow/Pillow@12.1.1...12.3.0)

---
updated-dependencies:
- dependency-name: pillow
  dependency-version: 12.3.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Fix version specification for Pillow in requirements.txt

Signed-off-by: Christopher Paolini <paolini@engineering.sdsu.edu>
Added debugging information to CMake configuration step.

Signed-off-by: Christopher Paolini <paolini@engineering.sdsu.edu>
New directory structure with new implementations for MOLE grids, arrays, errors, and utilities.  New README files in every subdirectory

fixed errors
Added debugging information to CMake configuration step.

Signed-off-by: Christopher Paolini <paolini@engineering.sdsu.edu>
New directory structure with new implementations for MOLE grids, arrays, errors, and utilities.  New README files in every subdirectory

fixed errors

grid basic struct

makegrid added

Including DEBUGING MODE

Adding testing and build utilities

CMakefiles are updated
Parses variadic <attribute, value> pairs into a gridRaw, validates dimensionality, cell counts, topology and isPeriodic length at parse time, then narrows into gridParams1D/2D/3D and calls makeGrid. Failures return a gridNull carrying the error stack. isPeriodic takes a pointer to a vector<bool> so its size can be checked against dim.
29 cases covering parse-time validation through runChecks and dimension dispatch through the gridBuilder macro, including isPeriodic size mismatches and out-of-order attributes.
Four examples covering 1D construction, 2D with periodicity, argument order independence, and parse-time error reporting.
@Tony-Drummond Tony-Drummond added Enhancement New feature or request Tests C++ Issues related to the MOLE C++ API Unit Testing Something fails in the auto-testing phases MOLE-2.0 Ideas and issues relating to the MOLE 2.0 development branch and or update. labels Aug 18, 2026
@Tony-Drummond
Tony-Drummond requested a review from jbrzensk August 18, 2026 18:57
dependabot Bot and others added 16 commits August 18, 2026 13:30
Bumps [pillow](https://github.com/python-pillow/Pillow) from 12.1.1 to 12.3.0.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](python-pillow/Pillow@12.1.1...12.3.0)

---
updated-dependencies:
- dependency-name: pillow
  dependency-version: 12.3.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Fix version specification for Pillow in requirements.txt

Signed-off-by: Christopher Paolini <paolini@engineering.sdsu.edu>
Bumps [pillow](https://github.com/python-pillow/Pillow) from 12.1.1 to 12.3.0.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](python-pillow/Pillow@12.1.1...12.3.0)

---
updated-dependencies:
- dependency-name: pillow
  dependency-version: 12.3.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Fix version specification for Pillow in requirements.txt

Signed-off-by: Christopher Paolini <paolini@engineering.sdsu.edu>
Fix version specification for Pillow in requirements.txt

Signed-off-by: Christopher Paolini <paolini@engineering.sdsu.edu>
Added the example/sys files
Adds gridBase::applyDebugMode and a debug_mode overload of each
grid1D/2D/3D constructor. The mode is applied once at construction
and is not stored.

The trigger is isValidatedGrid() rather than hasGridErrors(): a grid
carries MOLE_ERR_GRID_UNCHECKED until validation clears it, and
mergeErrors folds upstream errors into the same stack, so a valid
grid can hold errors it did not cause. Aborting on those would end
user programs over grids that are fine.

Reporting is non-destructive in every mode, so a caller can still
print or write the log afterwards.

Makes gridBase's constructor protected and gridNull's private,
reachable only through makeGridNull. Both classes are internal.

15 new tests, all binaries green.
Parses a debug pair carrying a MOLE debug mode and applies it to
the resulting grid. Dispatch moves into buildGrid so that every
path, including both failure paths, funnels through one point.

The debug pair has to come first: parsing stops at an unrecognized
attribute name, so a trailing debug pair is never read on the calls
that need it. Documented in the header and asserted in the tests.
Codes 14-17 mapped to empty strings; code 16 is logged by
gridBuilder on an unknown attribute, so users saw a bare code with
no message. Renumbers the dictionary comments (011-017, not
001-007) and corrects the gridBuilder range to 10-99.
This fixes an issue with the semantics of validatedGrid(). With this fix, if a grid fails during validation it is not marked as validated but it's also flagged as having errors.

Update CMakeLists.txt
@Tony-Drummond
Tony-Drummond force-pushed the cpp_dev/mole2_0_grid_implementation branch from 1667e51 to cf6fe07 Compare September 1, 2026 22:42
This fixes an issue with the semantics of validatedGrid(). With this fix, if a grid fails during validation it is not marked as validated but it's also flagged as having errors.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved critical and moderate issues affect builds, linking, validation, safety, and API correctness.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Reorganizes the C++ implementation for MOLE 2.0, adding grids, arrays, error handling, utilities, examples, tests, and packaging infrastructure.

Changes:

  • Adds 1D–3D grid, array, and grid-builder APIs.
  • Introduces error tracking and numerical utilities.
  • Adds CMake integration, documentation, examples, and regression tests.
File summaries
File Description
cpp/tests/utils/test_utils.cpp Tests numerical utilities.
cpp/tests/utils/README.md Documents utility tests.
cpp/tests/support/mole_test.h Provides the test harness.
cpp/tests/README.md Documents the test layout.
cpp/tests/grids/test_makeGrid.cpp Tests grid factory dispatch.
cpp/tests/grids/test_grid3D.cpp Tests 3D grids.
cpp/tests/grids/test_grid2D.cpp Tests 2D grids.
cpp/tests/grids/test_grid1D.cpp Tests 1D grids.
cpp/tests/grids/test_grid_builder.cpp Tests grid-builder behavior.
cpp/tests/grids/README.md Documents grid tests.
cpp/tests/errors/test_errors.cpp Tests error-stack operations.
cpp/tests/errors/README.md Documents error tests.
cpp/tests/CMakeLists.txt Defines regression test targets.
cpp/tests/arrays/test_arrays.cpp Tests array behavior.
cpp/tests/arrays/README.md Documents array tests.
cpp/src/utils/utils.cpp Implements numerical utilities.
cpp/src/utils/README.md Documents utility sources.
cpp/src/sys/README.md Documents system utilities.
cpp/src/sys/MOLE_Errors.cpp Implements error logging.
cpp/src/README.md Documents source organization.
cpp/src/include/utils.h Declares utility APIs.
cpp/src/include/README.md Documents public headers.
cpp/src/include/MOLE_grids.h Declares grid APIs and structures.
cpp/src/include/MOLE_errors.h Declares errors and debug modes.
cpp/src/include/MOLE_arrays.h Declares array APIs.
cpp/src/include/grid_builder.h Declares the grid builder.
cpp/src/grids/README.md Documents grid sources.
cpp/src/grids/MOLE_grids.cpp Implements grid construction and validation.
cpp/src/grids/MOLE_arrays.cpp Implements array wrappers.
cpp/src/grids/grid_builder.cpp Implements grid-builder parsing.
cpp/src/CMakeLists.txt Defines and installs the library.
cpp/README.md Documents the C++ project layout.
cpp/examples/README.md Documents examples.
cpp/examples/grids/README.md Lists grid examples.
cpp/examples/grids/gridBuilder2D_basic.cpp Demonstrates 2D grid building.
cpp/examples/grids/gridBuilder1D_basic.cpp Demonstrates 1D grid building.
cpp/examples/grids/gridBuilder_error_handling.cpp Demonstrates builder failures.
cpp/examples/grids/gridBuilder_arg_order.cpp Demonstrates argument ordering.
cpp/examples/grids/grid2D_basic.cpp Demonstrates direct 2D construction.
cpp/examples/grids/grid1D_basic.cpp Demonstrates direct 1D construction.
cpp/examples/grids/err_grid2D_basic.cpp Demonstrates grid errors.
cpp/examples/grids/CMakeLists.txt Builds grid examples.
cpp/CMakeLists.txt Configures dependencies and subprojects.
cpp/cmake/MOLEConfig.cmake.in Defines the package configuration template.
CMakeLists.txt Integrates the new C++ subtree.
.gitignore Ignores C++ build output.
Review details

Suppressed comments (12)

cpp/src/grids/MOLE_arrays.cpp:331

  • This reports value-equal arrays as unequal merely because their storage addresses differ, contradicting operator==. Implement inequality as return !(*this == other); and update the public header's identity-based description.
bool array2D::operator!=(const array2D& other) const {
    bool are_not_equal = false;
    if (data_.n_rows != other.data_.n_rows || 
        data_.n_cols != other.data_.n_cols) {      // compare sizes
        are_not_equal = true;
        } 
    else if (data_.memptr() != other.data_.memptr()) { // same ptr?
        are_not_equal = true;
    }
    return are_not_equal;

cpp/src/grids/MOLE_arrays.cpp:374

  • This reports value-equal arrays as unequal merely because their storage addresses differ, contradicting operator==. Implement inequality as return !(*this == other); and update the public header's identity-based description.
bool array3D::operator!=(const array3D& other) const {
    bool are_not_equal = false;
    if (data_.n_rows != other.data_.n_rows || 
        data_.n_cols != other.data_.n_cols || 
        data_.n_slices != other.data_.n_slices) {      // compare sizes
        are_not_equal = true;
        } 
    else if (data_.memptr() != other.data_.memptr()) { // same ptr?
        are_not_equal = true;
    }

cpp/src/grids/MOLE_grids.cpp:655

  • The curvilinear branch—and the adjacent nonuniform branch—checks only that both arrays are nonempty. A pair of 1×1 arrays with m = n = 3 is therefore marked validated while centers and faces remain empty, which can break downstream grid operations. Validate dimensional consistency and initialize all required layers before marking these grids valid.
    case 'c':  // User must provide at least nodal coordinates
        if (grid.nodes_X.data_.is_empty() || 
            grid.nodes_Y.data_.is_empty()){

cpp/src/grids/MOLE_grids.cpp:947

  • The curvilinear branch—and the nonuniform branch below it—accepts any three nonempty arrays without checking their (m+1,n+1,o+1) compatibility, then marks the grid validated with empty centers/faces. Validate the supplied geometry and construct or require every layer needed by downstream operators.
    case 'c':  // User must provide at least nodal coordinates
        if (grid.nodes_X.data_.is_empty() || grid.nodes_Y.data_.is_empty() ||
            grid.nodes_Z.data_.is_empty()){

cpp/src/grids/MOLE_grids.cpp:663

  • The nonuniform path also accepts any two non-empty arrays, including mismatched shapes unrelated to the declared cell counts, and leaves center/face layers unbuilt. Such a grid is not safe for downstream operators despite isValidatedGrid() returning true.
    case 'n':   // nonuniform grids require user-supplied nodes_X
        if (grid.nodes_X.data_.is_empty() || 
            grid.nodes_Y.data_.is_empty()) {

cpp/src/grids/MOLE_grids.cpp:957

  • Any three non-empty arrays make this nonuniform 3D grid valid, even when their shapes disagree with each other and the declared cell counts; center and face arrays also remain empty. Validate the topology's complete coordinate contract before reporting success.
    case 'n':   // nonuniform grids require user-supplied nodes_X
        if (grid.nodes_X.data_.is_empty() || grid.nodes_Y.data_.is_empty() ||
            grid.nodes_Z.data_.is_empty()) {

cpp/src/grids/MOLE_grids.cpp:93

  • Popping the source from newest to oldest and immediately pushing into the destination reverses the error history. Since printing also walks top-down, propagated errors appear in the opposite backtrace order; reverse through an intermediate stack before appending.
    stack<MOLE_Errors> tmp_stk = inerrs;
    while (!tmp_stk.empty()) {
        logGridErr(tmp_stk.top().errCode, tmp_stk.top().errLocation,
                    tmp_stk.top().paramError);
        tmp_stk.pop();

cpp/src/include/MOLE_errors.h:136

  • This diagnostic directs users to validateGrid(), but no such API exists; the implemented validation method is validGrid(). Reporting a nonexistent recovery action makes the new error handling misleading.
    {MOLE_ERR_GRID_UNCHECKED, 
    "Grid has not been validated, call validateGrid() first"},

cpp/src/include/MOLE_grids.h:66

  • Default construction leaves topology indeterminate. Constructing a grid without assigning this required field then reads an indeterminate value in validGrid() instead of reliably logging an invalid topology.
    char topology; // 'u'=uniform, 'c'=curvilinear, 'n'=non-uniform

cpp/src/include/MOLE_grids.h:89

  • Default construction leaves topology indeterminate. Constructing a grid without assigning this required field then reads an indeterminate value in validGrid() instead of reliably logging an invalid topology.
    char topology; // 'u'=uniform, 'c'=curvilinear, 'n'=non-uniform

cpp/src/include/README.md:40

  • The listed header name is misspelled; the installed/public file is utils.h.
+ **utilis.h**: header file for all MOLE utility functions, which

cpp/src/sys/README.md:26

  • The documented source filename is misspelled and does not match MOLE_Errors.cpp, so readers cannot locate the implementation.
+ **MOLE_Erros.cpp**: a C++ implementation of MOLE's error handling
  • Files reviewed: 45/46 changed files
  • Comments generated: 29
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread CMakeLists.txt
##add_subdirectory(tests/cpp)
#add_subdirectory(tests/matlab_octave)
#add_subdirectory(examples/cpp)
add_subdirectory(cpp)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also the cause of the GitHub action build errors. Change line 159 in CMakeLists.txt to

add_custom_target(cpp_all_build DEPENDS mole_C++ tests_C++ examples_C++ tests_matlab_octave)

To separate the cpp build from the total build. Then the actions will pass.

Comment thread cpp/CMakeLists.txt
Comment thread cpp/CMakeLists.txt
Comment on lines +77 to +78
file(DOWNLOAD ${SUPERLU_TARBALL_URL} ${BUILD_DIR}/superlu-${SUPERLU_VERSION}.tar.gz)
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xzf superlu-${SUPERLU_VERSION}.tar.gz WORKING_DIRECTORY ${BUILD_DIR})

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If implemented, check to make sure this doesn't break the build sequence.

Comment thread cpp/CMakeLists.txt
add_subdirectory(examples/grids)

# Custom target to build everything
add_custom_target(all_build DEPENDS mole_C++ tests_C++ examples_C++ tests_matlab_octave)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ohh, see previous comments

Comment thread cpp/src/CMakeLists.txt
Comment on lines +21 to +22
# target_link_libraries(mole PUBLIC ${ARMADILLO_LIBRARIES})
# target_include_directories(mole PUBLIC ${ARMADILLO_INCLUDE_DIRS})
Comment thread cpp/src/include/utils.h
Comment on lines +125 to +126
void read_ErrorLog(int ErrorCode, std::string &location,
std::string &arrayName);
Comment on lines +175 to +176
string filename = logType + getDateTimeString();
std::ofstream outFile(filename);
Comment thread cpp/src/utils/utils.cpp
Comment on lines +84 to +89
void Utils::read_ErrorLog(int ErrorCode, std::string &location,
std::string &arrayName) {
if (!errs.empty()) {
MOLE_Errors topError = errs.top();
ErrorCode = topError.errCode;
location = topError.errLocation;
Comment thread cpp/src/utils/utils.cpp
Comment on lines +289 to +296
mat sheet(m, n, fill::zeros);

// Build X
vec t(n, fill::ones);

X.zeros(m, n, o);
Y.zeros(m, n, o);
Z.zeros(m, n, o);
Comment thread cpp/tests/CMakeLists.txt
target_include_directories(${_mole_test_bin} PRIVATE support)
target_compile_options(${_mole_test_bin} PRIVATE -Wall -Wextra)

add_test(NAME ${_mole_test_name} COMMAND ${_mole_test_bin})

@jbrzensk jbrzensk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a handful of CoPilot comments, some of mine, and a Claude explanation of the int vs reference issue. Basically the one "have-to" fix is the CMakeLists.txt to change the name so the GitHub actions pass.

Maybe the other I do not understand is the error-codes stacked with the vectors, but only treated as vectors later. I think those may need a validation check to make sure math works.

template <typename ArrayT>
void drainArrayErrors(ArrayT& a) {
while (a.hasArrayErrors()) {
int errCode;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be initialized to something?


if (grid.m <= 0 || grid.n <= 0) {
logGridErr(MOLE_ERR_INVALID_GRID_SIZE, "grid2D[construct]",
to_string(grid.m));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If grid.n is wrong, reports grid.m

string logType){
// create a string for the filename
string filename = logType + getDateTimeString();
std::ofstream outFile(filename);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should check if file is open before writing.

// carries the array's own error stack along with its data. A null
// pointer means the attribute was not supplied, so dst keeps its
// default and MOLE reads it as unset.
template <typename T>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the array1D is a stacked object with the error stack and the vector in one shot. Later, you assume the error stack is empty, and start copying and pasting this around.

There should be some check, somewhere, that the error stack is empty and the size of this is the size of a std::vector.

Comment thread cpp/src/utils/utils.cpp
// Armadillo vecs and mats. It losgs and error when the two input
// vectors are not the same size.
//
double Utils::trapz(const vec &x, const vec &y) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs a validation check the x and y are not 0, or something wierd.

if (grid.m <= 0 || grid.n <= 0) {
logGridErr(MOLE_ERR_INVALID_GRID_SIZE, "grid2D[construct]",
to_string(grid.m));
isValid = false;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And shouldn't the function break here if "isValid=False"?. Why keep building?

bool isValid = true;

if (grid.m <= 0 || grid.n <= 0 || grid.o <=0) {
logGridErr(MOLE_ERR_INVALID_GRID_SIZE, "grid3D[construct]",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same error as 2D, and should break out of function if grid is not valid.

// user facing class like grids or operators. Users can also print the
// errors to standard output or write them to a file.
//
void array1D::read_ErrorLog(int ErrorCode, std::string &location,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be int &ErrorCode, but it still works?

I ran it though claude and this was it's response:


Confirmed — MOLEerr_print at [MOLE_Errors.cpp:158-162] does exactly that fallback: if err.errCode isn't a key in MOLE_errors_messages, it prints "Invalid MOLE Error code []". Now the full chain, precisely:

The by-value vs by-reference mismatch:

read_ErrorLog's signature ([MOLE_arrays.cpp:194]):

void array1D::read_ErrorLog(int ErrorCode, std::string &location, std::string &arrayName)
Only location and arrayName are references (&). ErrorCode is a plain int — a copy. Inside the function ([MOLE_arrays.cpp:198].

ErrorCode = topError.errCode; // writes to the LOCAL copy
location = topError.errLocation; // writes through the reference — caller sees this
arrayName = topError.paramError; // writes through the reference — caller sees this
ErrorCode = topError.errCode; mutates the function's own stack-local parameter. When the function returns, that copy is discarded — nothing propagates back to the caller. location and arrayName do propagate, because they're bound to the caller's actual variables via reference. So the function silently does 2 of its 3 "outputs" correctly and drops the third — which is easy to miss in review because the code reads as if all three behave the same way.

The caller, and exactly what your selection shows:

drainArrayErrors<ArrayT> ([MOLE_grids.h:162-169]):


template <typename ArrayT> void drainArrayErrors(ArrayT &a) {
    while (a.hasArrayErrors()) {
        int errCode;                              // <- line 164, uninitialized
        string location, msgparam;
        a.read_ErrorLog(errCode, location, msgparam);
        logGridErr(errCode, location, msgparam);
    }
}

int errCode; at line 164 (the line you have selected) is declared with no initializer — its value is indeterminate stack garbage until something writes to it. The call to read_ErrorLog looks like it should fill it in (that's clearly the intent, matching location/msgparam), but because the parameter is by-value, it never does. So by the time logGridErr(errCode, ...) runs, errCode still holds whatever bytes happened to be sitting in that stack slot — reading it at all is undefined behavior in the strict sense, and in practice it's just whatever leftover value was there.

Downstream effect: logGridErr(size_t errCode, ...) ([MOLE_grids.h:141]) takes size_t, so the garbage int gets converted (sign-extended/wrapped if negative) into some arbitrary size_t and pushed onto the grid's real, user-visible error stack — this is the stack that print_ErrorLog()/write_ErrorLog() actually report from. The location and paramError strings attached to that entry are correct (e.g. "nd2DGrid" and a real descriptive message), but the errCode field is nonsense.

Concrete trigger path: grid2D::validGrid() → buildOrCheck2DCoords → nd2DGrid logs a real error like MOLE_ERR_INVALID_ARRAY_SIZE onto the array's own private a_errs stack when, e.g., mismatched coordinate array sizes are detected. buildOrCheck2DCoords then calls drainArrayErrors(X) to move that error onto the grid's stack — and that's the exact call that goes through the broken read_ErrorLog. So the grid ends up "flagged invalid" correctly (that part uses a separate mechanism, setCheckedWithErrors/MOLE_ERR_GRID_FLAGGED_W_ERRS), but if a user calls print_ErrorLog() to find out why, instead of seeing MOLE_ERR_INVALID_ARRAY_SIZE with its real message, they see "Invalid MOLE Error code [garbage]" — the diagnostic that was supposed to explain the failure is destroyed in transit.

The fix is mechanical: change all three read_ErrorLog declarations (and Utils::read_ErrorLog, same bug at [utils.h:125]/[utils.cpp:84]) from int ErrorCode to int &ErrorCode, matching the reference pattern already used for location/arrayName right next to it.

@Tony-Drummond

Copy link
Copy Markdown
Collaborator Author

I'm closing this PR after re-organizing the mole directory structure with only MOLE 2.0 source code. For now, MOLE v1.2 files are being relocated (with 'git mv') in a temporary subdirectory and these files will be either refactored or replaced by the actual files as the MOLE 2.0 implementation progresses.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

C++ Issues related to the MOLE C++ API Enhancement New feature or request MOLE-2.0 Ideas and issues relating to the MOLE 2.0 development branch and or update. Tests Unit Testing Something fails in the auto-testing phases

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants